package btanalyzer.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import btanalyzer.core.DiggResultDiggedSite.DRCategory;

@Component
public class DiggerImpl implements Digger, InitializingBean {

//	private static final String VIDEO_CAT = "video";
//	private static final String GAMES_CAT = "games";
//	private static final String PROGRAMS_CAT = "programs";
//
//	private static final String GAMES_CAT_URL_PREFIX = "http://thepiratebay.org/browse/400/";
//	private static final String VIDEO_CAT_URL_PREFIX = "http://thepiratebay.org/browse/200/";
//	private static final String PROGRAMS_CAT_URL_PREFIX = "http://thepiratebay.org/browse/300/";
//
//	private static final String BY_SEEDS_SUFFIX = "/7";

	/**
	 * 
	 * @author Pawel
	 * 
	 */
	private interface DiggingOperation {

		/**
		 * Proceeds operation.
		 * 
		 * @param text
		 *            line which is digged
		 * @return true if information was digged
		 */
		boolean proceed(String text);

		/**
		 * 
		 * @return matching text retrieved during last proceed() returning true
		 */
		String lastMatch();

	}

	/**
	 * Implementations of {@link DiggingOperation}.
	 */
	private enum DiggingOperationImpl implements DiggingOperation {
		URL("urlDiggingOperation"), DETAILS("detailDiggingOperation"), SEEDS(
				"seedsDiggingOperation"), PEERS("peersDiggingOperation");

		private Pattern pattern;
		private String lastMatch;
		private String name;

		private DiggingOperationImpl(String name) {
			this.name = name;
		}

		public void setRegex(String regex) {
			this.pattern = Pattern.compile(regex);
		}

		public boolean proceed(String text) {
			Matcher matcher = pattern.matcher(text);
			if (matcher.matches()) {
				lastMatch = matcher.group(1);
				return true;
			}
			return false;
		}

		public String lastMatch() {
			return lastMatch;
		}

		@Override
		public String toString() {
			return name;
		}

	}

	// logger
	private static Logger log = LoggerFactory.getLogger(DiggerImpl.class);

	private static final String UNRESOLVED_PREFIX = "torrent_";
	private long nameCnt = 0L;

	//
	// private fields
	//
	private DiggingOperationImpl actualDiggingOperation;

	private Pattern metadataFileNamePattern;

	//
	// properties
	//

	@Value("${digger.metadataFileNameRegex}")
	private String metadataFileNameRegex;

	@Value("${digger.urlDiggRegex}")
	private String urlDiggRegex;

	@Value("${digger.detailsDiggRegex}")
	private String detailsDiggRegex;

	@Value("${digger.peersDiggRegex}")
	private String peersDiggRegex;

	@Value("${digger.seedsDiggPattern}")
	private String seedsDiggRegex;

	public void setMetadataFileNameRegex(String metadataFileNameRegex) {
		this.metadataFileNameRegex = metadataFileNameRegex;
	}

	public void setUrlDiggRegex(String urlDiggRegex) {
		this.urlDiggRegex = urlDiggRegex;
	}

	public void setDetailsDiggRegex(String detailsDiggRegex) {
		this.detailsDiggRegex = detailsDiggRegex;
	}

	public void setPeersDiggRegex(String peersDiggRegex) {
		this.peersDiggRegex = peersDiggRegex;
	}

	public void setSeedsDiggRegex(String seedsDiggRegex) {
		this.seedsDiggRegex = seedsDiggRegex;
	}

	//
	// public methods
	//

	public void afterPropertiesSet() throws Exception {
		DiggingOperationImpl.URL.setRegex(urlDiggRegex);
		DiggingOperationImpl.DETAILS.setRegex(detailsDiggRegex);
		DiggingOperationImpl.PEERS.setRegex(peersDiggRegex);
		DiggingOperationImpl.SEEDS.setRegex(seedsDiggRegex);
		metadataFileNamePattern = Pattern.compile(metadataFileNameRegex);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see btanalyzer.core.Digger#digg(java.lang.String, java.lang.String)
	 */
	public List<DiggResult> digg(String htmlDirPath) {
		
		// html files to digg
		File htmlDir = new File(htmlDirPath);
		File[] htmlFiles = htmlDir.listFiles();
		
		// return values
		List<DiggResult> ret = new ArrayList<DiggResult>();
		if (htmlFiles == null) {
			log.warn("No files found in dir={}", htmlDir.getAbsolutePath());
		}
		
		for (File htmlFile : htmlFiles) {
			
			nameCnt = 0L;
			
			DiggResult result = new DiggResult();
			DiggResultDiggedSite diggedSite = new DiggResultDiggedSite();
			ret.add(result);
			result.setDiggResultDiggedSite(diggedSite);

			diggedSite.setUrl("http://" + htmlFile.getName().replace("_", "/"));
			diggedSite.setHtmlFilePath(htmlFile.getPath());
			DRCategory category = null;
			if (htmlFile.getName().startsWith("thepiratebay.org_browse_200_")) {
				category = DRCategory.VIDEO;
			}
			if (htmlFile.getName().startsWith("thepiratebay.org_browse_300_")) {
				category = DRCategory.PROGRAMS;
			}
			if (htmlFile.getName().startsWith("thepiratebay.org_browse_400_")) {
				category = DRCategory.GAMES;
			}
			diggedSite.setCategory(category);
			
			// open file to digg
			BufferedReader bf = null;
			try {
				bf = new BufferedReader(new InputStreamReader(
						new FileInputStream(htmlFile)));
			} catch (FileNotFoundException e) {
				log.error("Error creating BufferedReader form file={}", 
						htmlFile.getAbsolutePath());
				throw new RuntimeException(e);
			}
			
			// digg
			String torrentUrl = null;
			String torrentFileName = null;
			String torrentDetails = null;
			Integer torrentSeeds = null;
			Integer torrentPeers = null;
			String line = null; // zadeklarowane tu, dla wypisywania błędów
			actualDiggingOperation = DiggingOperationImpl.URL;
			try {
				for (line = bf.readLine(); line != null; line = bf.readLine()) {
					if (actualDiggingOperation.proceed(line)) {
						String match = actualDiggingOperation.lastMatch();
						switch (actualDiggingOperation) {
						case URL:
							String[] res = torrentMetadataFileNameFromURL(match);
							torrentUrl = res[0];
							torrentFileName = res[1];
							break;
						case DETAILS:
							torrentDetails = match;
							break;
						case SEEDS:
							try {
								torrentSeeds = Integer.parseInt(match);
							} catch (NumberFormatException e) {
								log.error("Error while parsing seeds numer={}, for torrent={}", 
										match, torrentFileName);
							}
							break;
						case PEERS:
							try {
								torrentPeers = Integer.parseInt(match);
							} catch (NumberFormatException e) {
								log.error("Error while parsing peers numer={}, for torrent={}", 
										match, torrentFileName);
							}
							DiggResultTorrent torrent = new DiggResultTorrent();
							torrent.setDetails(torrentDetails);
							torrent.setFileName(htmlFile.getName().split("\\.html")[0] + "_" +torrentFileName);
							torrent.setPeers(torrentPeers);
							torrent.setSeeds(torrentSeeds);
							torrent.setUrl(torrentUrl);
							result.addDiggResultTorrent(torrent);
							break;
						default:
							log.error("unknown diggingOperation={}", actualDiggingOperation);
							break;
						}
						nextDigger();
					}
				}
			} catch (Exception e) {
				log.error("error reading url=" + diggedSite.getUrl() + 
						" content, line={}", line, e);
				throw new RuntimeException(e);
			} 
			
			try {
				bf.close();
			} catch (IOException e) {
				log.warn("error closing bufferedReader for url=" + 
						diggedSite.getUrl(), e);
			}
			
		}
		return ret;
	}
		
	/**
	 * 
	 * @param url
	 * @return name of metadata file
	 */
	public String[] torrentMetadataFileNameFromURL(String url) {

		// create matcher
		Matcher matcher = metadataFileNamePattern.matcher(url);

		// matches must be invoked
		if (matcher.matches()) {

			String[] ret = new String[matcher.groupCount()];
			for (int i = 0; i < matcher.groupCount(); i++) {
				if (i == matcher.groupCount() - 1) {
					ret[i] = nameCnt++ + "___" + matcher.group(i + 1);
				} else {
					ret[i] = matcher.group(i + 1);
				}
			}
			return ret;
		}

		// if doesn't matches return unresolved name
		String[] ret = { url, nameCnt++ + "___" + UNRESOLVED_PREFIX };
		log.warn("url={} doesn't match regexp={}, torrentFileName={}",
				new Object[] { url, metadataFileNameRegex, ret[1] });
		return ret;
	}

	private void nextDigger() {
		switch (actualDiggingOperation) {
		case URL:
			actualDiggingOperation = DiggingOperationImpl.DETAILS;
			break;
		case DETAILS:
			actualDiggingOperation = DiggingOperationImpl.SEEDS;
			break;
		case SEEDS:
			actualDiggingOperation = DiggingOperationImpl.PEERS;
			break;
		case PEERS:
			actualDiggingOperation = DiggingOperationImpl.URL;
			break;
		}
	}

}
